import type { ComponentStyleIdType, PlatformTypeValue } from '../../../types/interfaces'
import { BaseModule } from '../../../lib/abstract'
import { PageHandler } from '../../../lib/page_handler'
import { GiteePageType } from '../../../lib/page_handler/platform/gitee/types'
import { PollingHandler } from '../../../lib/polling_handler'
import { ToolKit } from '../../../lib/utils/toolkit'
import {
  ComponentStyleId,
  HOSTNAME,
  logger,
  PROJECT_PREFIX,
  SUPPORTED_PLATFORMS,
} from '../../../types/constants'
import { SYMBOLS } from '../../../types/interfaces'
import { ConfigManager } from '../../config_manager'

interface Config {
  enabled?: boolean
  iconSize?: FileIconSize
}

interface FileIconsFilename {
  ($element: JQuery<HTMLElement>): string | null
}
enum FileIconsInsertPosition {
  Before = 'before',
  Prepend = 'prepend',
}

interface FileIconsConfig {
  selectors: {
    folder: string
    file: string
    icon: string
  }
  fileMarginRight?: number
  filename: FileIconsFilename
  insertPosition: FileIconsInsertPosition
  listen?: boolean
}

type FileIconsPlatformConfig = Partial<Record<PlatformTypeValue, Record<string, FileIconsConfig>>>

export enum FileIconSize {
  sm = 'sm',
  md = 'md',
  lg = 'lg',
}

export interface FileIconConfig {
  enabled: boolean
  iconSize: FileIconSize
  showFileType: boolean
}

export interface FileMetadata {
  icon: string
  color?: string
  description: string
}

export interface FileTypeMapping extends FileMetadata {
  pattern: RegExp
}

export interface FileIconInfo {
  element: JQuery<HTMLElement>
  fileName: string
  fileExtension: string
  icon: string
  color?: string
}

/**
 * 默认文件图标配置
 */
const DEFAULT_FILE_ICONS: Record<'folder' | 'file', FileMetadata> = {
  folder: {
    icon: 'nf-oct-file_directory_open_fill',
    color: '#ffb02e',
    description: '文件夹',
  },
  file: {
    icon: 'nf-cod-file',
    description: '普通文件',
    color: '#9E9E9E',
  },
}

/**
 * 配置文件类型映射（https://www.nerdfonts.com/cheat-sheet）
 */
const FILE_TYPE_MAPPINGS: FileTypeMapping[] = [
  // 脚本文件
  { pattern: /\.(sh|bash|zsh)$/i, icon: 'nf-seti-shell', description: 'Shell 脚本', color: '#4CAF50' },
  { pattern: /\.(py|pyc|pyo|pyd)$/i, icon: 'nf-fae-python', description: 'Python 脚本', color: '#3776AB' },
  { pattern: /\.(js|mjs|cjs)$/i, icon: 'nf-fa-square_js', description: 'JavaScript 文件', color: '#F7DF1E' },
  { pattern: /\.(ts|tsx)$/i, icon: 'nf-seti-tsconfig', description: 'TypeScript 文件', color: '#3178C6' },
  { pattern: /\.(jsx)$/i, icon: 'nf-dev-react', description: 'React 组件', color: '#61DAFB' },
  { pattern: /\.(vue)$/i, icon: 'nf-seti-vue', description: 'Vue 组件', color: '#4FC08D' },
  { pattern: /\.(php)$/i, icon: 'nf-dev-php', description: 'PHP 文件', color: '#777BB4' },
  { pattern: /\.(rb|erb)$/i, icon: 'nf-cod-ruby', description: 'Ruby 文件', color: '#CC342D' },
  { pattern: /\.(go)$/i, icon: 'nf-seti-go', description: 'Go 文件', color: '#00ADD8' },
  { pattern: /\.(rs)$/i, icon: 'nf-dev-rspec', description: 'Rust 文件', color: '#000000' },
  { pattern: /\.(java)$/i, icon: 'nf-dev-java', description: 'Java 文件', color: '#007396' },
  { pattern: /\.(c|cpp|cc|cxx|c\+\+)$/i, icon: 'nf-dev-c_lang', description: 'C/C++ 文件', color: '#00599C' },
  { pattern: /\.(cs)$/i, icon: 'nf-md-language_csharp', description: 'C# 文件', color: '#239120' },
  { pattern: /\.(swift)$/i, icon: 'nf-dev-swift', description: 'Swift 文件', color: '#FA7343' },
  { pattern: /\.(kt|kts)$/i, icon: 'nf-dev-kotlin', description: 'Kotlin 文件', color: '#7F52FF' },
  { pattern: /\.(lua)$/i, icon: 'nf-dev-lua', description: 'Lua 文件', color: '#000080' },
  { pattern: /\.(pl|pm)$/i, icon: 'nf-dev-perl', description: 'Perl 文件', color: '#39457E' },
  { pattern: /\.(r)$/i, icon: 'nf-dev-r', description: 'R 语言文件', color: '#276DC3' },
  { pattern: /\.(scala)$/i, icon: 'nf-dev-scala', description: 'Scala 文件', color: '#DC322F' },
  { pattern: /\.(hs|lhs)$/i, icon: 'nf-dev-haskell', description: 'Haskell 文件', color: '#5D4F85' },
  { pattern: /\.(el|elc)$/i, icon: 'nf-dev-emacs', description: 'Emacs Lisp 文件', color: '#9266CC' },
  { pattern: /\.(ex|exs)$/i, icon: 'nf-dev-elixir', description: 'Elixir 文件', color: '#4E2A8E' },
  { pattern: /\.(erl|hrl)$/i, icon: 'nf-dev-erlang', description: 'Erlang 文件', color: '#A90533' },
  { pattern: /\.(clj|cljs|cljc|edn)$/i, icon: 'nf-dev-clojure', description: 'Clojure 文件', color: '#5881D8' },

  // 构建工具
  { pattern: /^(Makefile|GNUmakefile)$/i, icon: 'nf-seti-makefile', description: 'Makefile' },
  { pattern: /\.(mk|mak)$/i, icon: 'nf-seti-makefile', description: 'Makefile' },
  { pattern: /^(Dockerfile)$/i, icon: 'nf-seti-docker', description: 'Dockerfile', color: '#2496ED' },
  { pattern: /\.dockerfile$/i, icon: 'nf-seti-docker', description: 'Dockerfile', color: '#2496ED' },
  { pattern: /^(compose\.yml|compose\.yaml|docker-compose\.yml|docker-compose\.yaml)$/i, icon: 'nf-dev-docker', description: 'Docker Compose', color: '#2496ED' },
  { pattern: /\.(gradle|gradle\.kts)$/i, icon: 'nf-dev-gradle', description: 'Gradle 构建文件', color: '#02303A' },
  { pattern: /\.(xml|pom)$/i, icon: 'nf-dev-maven', description: 'Maven 构建文件', color: '#C71A36' },
  { pattern: /package\.json$/i, icon: 'nf-dev-npm', description: 'npm 包配置', color: '#CB3837' },
  { pattern: /package-lock\.json$/i, icon: 'nf-dev-npm', description: 'npm 锁文件', color: '#CB3837' },
  { pattern: /yarn\.lock$/i, icon: 'nf-dev-yarn', description: 'Yarn 锁文件', color: '#2C8EBB' },
  { pattern: /pnpm-lock\.yaml$/i, icon: 'nf-dev-pnpm', description: 'pnpm 锁文件', color: '#F69220' },
  { pattern: /\.(gemfile|gemfile\.lock)$/i, icon: 'nf-cod-ruby', description: 'Ruby Bundler', color: '#CC342D' },
  { pattern: /\.(cargo\.toml|cargo\.lock)$/i, icon: 'nf-dev-rust', description: 'Rust Cargo', color: '#000000' },
  { pattern: /\.(go\.mod|go\.sum)$/i, icon: 'nf-seti-go', description: 'Go 模块', color: '#00ADD8' },
  { pattern: /\.(cmake|cmakelists\.txt)$/i, icon: 'nf-dev-cmake', description: 'CMake', color: '#064F8C' },
  { pattern: /\.(bazel|bazelrc|build|workspace)$/i, icon: 'nf-seti-bazel', description: 'Bazel', color: '#43A047' },

  // Web 文件
  { pattern: /\.(html|htm|xhtml)$/i, icon: 'nf-dev-html5', description: 'HTML 文件', color: '#E34F26' },
  { pattern: /\.(css|scss|sass|less|styl)$/i, icon: 'nf-seti-css', description: '样式文件', color: '#1572B6' },
  { pattern: /\.(md|markdown|mdown|mkdn)$/i, icon: 'nf-oct-markdown', description: 'Markdown 文档', color: '#548af7' },
  { pattern: /\.(svg)$/i, icon: 'nf-seti-svg', description: 'SVG 图像', color: '#FFB13B' },

  // 数据文件
  { pattern: /\.(csv|tsv)$/i, icon: 'nf-fa-file_csv', description: 'CSV 数据', color: '#1E6E3E' },
  { pattern: /\.(sql)$/i, icon: 'nf-dev-sqlite', description: 'SQL 文件', color: '#336791' },
  { pattern: /\.(db|sqlite|sqlite3|mdb)$/i, icon: 'nf-seti-db', description: '数据库文件', color: '#003B57' },

  // 文档文件
  { pattern: /\.(txt)$/i, icon: 'nf-md-format_text', description: '文本文件', color: '#888888' },
  { pattern: /\.(pdf)$/i, icon: 'nf-seti-pdf', description: 'PDF 文档', color: '#F40F02' },
  { pattern: /\.(doc|docx|docm)$/i, icon: 'nf-seti-word', description: 'Word 文档', color: '#2B579A' },
  { pattern: /\.(xls|xlsx|xlsm)$/i, icon: 'nf-md-microsoft_excel', description: 'Excel 文档', color: '#217346' },
  { pattern: /\.(ppt|pptx|pptm)$/i, icon: 'nf-fa-file_powerpoint', description: 'PowerPoint 文档', color: '#D24726' },

  // 压缩文件
  { pattern: /\.(zip|rar|7z|tar|gz|bz2|xz)$/i, icon: 'nf-cod-file_zip', description: '压缩文件', color: '#FF9800' },

  // 媒体文件
  { pattern: /\.(jpg|jpeg|png|gif|bmp|webp|ico)$/i, icon: 'nf-fa-image', description: '图像文件', color: '#FF4081' },
  { pattern: /\.(mp3|wav|flac|aac|ogg)$/i, icon: 'nf-fa-file_sound_o', description: '音频文件', color: '#9C27B0' },
  { pattern: /\.(mp4|avi|mov|wmv|flv|webm)$/i, icon: 'nf-fa-file_video', description: '视频文件', color: '#FF5252' },

  // 特殊文件
  { pattern: /\.gitignore$/i, icon: 'nf-fa-git_square', description: 'Git 忽略文件', color: '#F14E32' },
  { pattern: /\.gitattributes$/i, icon: 'nf-fa-git_square', description: 'Git 属性文件', color: '#F14E32' },
  { pattern: /\.gitmodules$/i, icon: 'nf-fa-git_square', description: 'Git 子模块', color: '#F14E32' },
  { pattern: /\.(LICENSE|LICENCE|COPYING)$/i, icon: 'nf-md-license', description: '许可证文件', color: '#8BC34A' },
  { pattern: /\.(README)$/i, icon: 'nf-fa-readme', description: '说明文档', color: '#1976D2' },
  { pattern: /\.(CHANGELOG|HISTORY)$/i, icon: 'nf-cod-history', description: '更新日志', color: '#FFA000' },

  // 配置文件（顺序放到后面，因为其他分类存在同文件名后缀的文件，避免其他分类无法匹配）
  { pattern: /\.(json|jsonc|json5)$/i, icon: 'nf-md-code_json', description: 'JSON 配置文件', color: '#b589ec' },
  { pattern: /\.(yml|yaml)$/i, icon: 'nf-dev-yaml', description: 'YAML 配置文件' },
  { pattern: /\.toml$/i, icon: 'nf-custom-toml', description: 'TOML 配置文件' },
  { pattern: /\.xml$/i, icon: 'nf-md-xml', description: 'XML 配置文件' },
  { pattern: /\.env$/i, icon: 'nf-cod-server_environment', description: '环境配置文件' },
  { pattern: /\.config$/i, icon: 'nf-seti-config', description: '配置文件' },

  // 默认文件类型（最后匹配）
  { pattern: /.*/, ...DEFAULT_FILE_ICONS.file },
]

export class FileIcons extends BaseModule {
  readonly id: string = 'file_icons'
  readonly styleId: string = ComponentStyleId[this.name]
  isInitialized: boolean = false
  private static instance: FileIcons
  private readonly className: string = `${PROJECT_PREFIX}-file-icon`
  private readonly DEFAULT_FILE_MARGIN_RIGHT: number = 0
  private readonly configManager: ConfigManager = ConfigManager.getInstance()
  readonly supportedPlatforms: string[] = this.configManager.moduleSupportedPlatforms(this.name)

  get currentConfig(): FileIconsConfig | undefined {
    return this.getCurrentConfig()
  }

  // 存储处理过的文件信息
  private processedFiles: Map<JQuery<HTMLElement>, FileIconInfo> = new Map()

  config: Config = {
    enabled: true,
    iconSize: FileIconSize.md,
  }

  get name(): ComponentStyleIdType {
    return 'FileIcons'
  }

  /**
   * 获取单例实例
   */
  public static getInstance(): FileIcons {
    if (!FileIcons.instance) {
      FileIcons.instance = new FileIcons()
    }
    return FileIcons.instance
  }

  handleCleanupReferences(): void {
    this.processedFiles.clear()
  }

  core(): void {
    this.loadConfig()

    if (!this.config.enabled)
      return

    this.applyFileIcons()
  }

  onToggleCallback(_id: string, checked: boolean, first: boolean): void {
    super.onToggleCallback(_id, checked, first)
  }

  /**
   * 获取当前页面的配置
   */
  getCurrentConfig(): FileIconsConfig | undefined {
    const config: FileIconsPlatformConfig = {
      [SUPPORTED_PLATFORMS.GITEE]: {
        [GiteePageType.Project]: {
          selectors: {
            folder: '#tree-slider .tree-item[data-type="folder"] .tree-list-item .tree-folder-item',
            file: '#tree-slider .tree-item[data-type="file"] .tree-list-item .tree-folder-item',
            icon: '.iconfont',
          },
          filename: ($element: JQuery<HTMLElement>) => $element.attr('data-path')?.trim() || null,
          insertPosition: FileIconsInsertPosition.Before,
        },
        [GiteePageType.CodeView]: {
          selectors: {
            folder: '#project-wrapper .project-tree .el-tree-node .custom-folder-node',
            file: '#project-wrapper .project-tree .el-tree-node .custom-left-node',
            icon: 'i[class^="el-icon"], i.iconfont',
          },
          fileMarginRight: 4,
          filename: ($element: JQuery<HTMLElement>) => $element.find('> div').text()?.trim() || null,
          insertPosition: FileIconsInsertPosition.Prepend,
          listen: true,
        },
      },
    }

    const currentConfig = config[HOSTNAME]?.[PageHandler.getCurrentPageType()]

    if (!currentConfig) {
      logger.warn(`[${this.name}] 未找到当前页面类型的配置`)
    }

    return currentConfig
  }

  applyFileIcons(): void {
    if (!this.currentConfig)
      return

    this.cleanup()

    const { selectors, filename, listen } = this.currentConfig
    const { folder, file } = selectors

    if (!listen) {
      this.handleFolder(folder)
      this.handleFile(file, filename)
    }
    else {
      const pollingHandler = PollingHandler.getInstance()

      pollingHandler.api.awaitElement(folder, () => this.handleFolder(folder))
      pollingHandler.api.awaitElement(file, () => this.handleFile(file, filename))
    }
  }

  /**
   * 处理文件夹
   */
  handleFolder(folderSelector: string): void {
    const $folder = $(folderSelector)
    const folderCount = $folder.length

    if (folderCount === 0) {
      logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 未找到文件夹元素，跳过处理`, folderSelector)
      return
    }

    $folder.each((_index, element) => {
      const $element = $(element)

      this.processFolderElement($element)
    })

    logger.info(`[${this.name}] ${SYMBOLS.STATUS.SUCCESS} 已为 ${folderCount} 个文件夹添加图标`)
  }

  /**
   * 处理文件
   */
  handleFile(fileSelector: string, filename: FileIconsFilename): void {
    const $file = $(fileSelector)
    const fileCount = $file.length

    if (fileCount === 0) {
      logger.info(`[${this.name}] 未找到文件元素，跳过处理`, fileSelector)
      return
    }

    $file.each((_index, element) => {
      const $element = $(element)

      this.processFileElement($element, filename)
    })

    const fileData = Array.from(this.processedFiles.values())

    const fileTypes = new Set(fileData.map(item => item.fileExtension).filter(Boolean))
    logger.info(`[${this.name}] ${SYMBOLS.OTHERS.VIEWED} 已识别到 ${fileTypes.size} 种文件类型`, fileTypes)

    const fileDetails = fileData
      .map(({ element, ...rest }) => rest)
      .sort((a, b) => a.fileExtension.localeCompare(b.fileExtension))
    logger.info(`[${this.name}] ${SYMBOLS.STATUS.SUCCESS} 已为 ${fileCount} 个文件添加图标`, fileDetails)
  }

  /**
   * 处理文件夹元素
   */
  private processFolderElement($element: JQuery<HTMLElement>): void {
    this.addIconToElement($element, DEFAULT_FILE_ICONS.folder)
  }

  /**
   * 处理文件元素
   */
  private processFileElement($element: JQuery<HTMLElement>, filename: FileIconsFilename): void {
    const fileName = filename($element)

    if (!fileName)
      return

    const fileInfo = this.getFileInfo(fileName)
    this.addIconToElement($element, fileInfo)

    this.processedFiles.set($element, {
      element: $element,
      fileName,
      fileExtension: ToolKit.browser.file.getFileExtension(fileName),
      icon: fileInfo.icon,
      color: fileInfo.color,
    })
  }

  /**
   * 获取文件信息
   */
  private getFileInfo(fileName: string): FileMetadata {
    // 检查预设映射
    for (const mapping of FILE_TYPE_MAPPINGS) {
      if (mapping.pattern.test(fileName)) {
        return {
          icon: mapping.icon,
          color: mapping.color,
          description: mapping.description,
        }
      }
    }

    // 默认文件
    return DEFAULT_FILE_ICONS.file
  }

  /**
   * 添加图标到元素
   */
  private addIconToElement($element: JQuery<HTMLElement>, metadata: FileMetadata): void {
    const { icon, color, description } = metadata

    const emptyIcon = Boolean(icon)

    if (!emptyIcon)
      return

    this.handleIcon($element, true)

    // 创建图标元素
    const $icon = $(`
      <i class="${this.className} nf ${icon} ${this.getIconSizeClass()}" 
        style="margin-right: ${this.currentConfig?.fileMarginRight || this.DEFAULT_FILE_MARGIN_RIGHT}px !important;font-size: ${this.getIconSizeValue()} !important; ${color ? `color: ${color} !important;` : ''}"
        title="${description}">
      </i>
    `)

    if (this.currentConfig?.insertPosition === FileIconsInsertPosition.Prepend)
      $element.prepend($icon)
    else
      $element.before($icon)
  }

  /**
   * 处理图标显示状态
   */
  handleIcon($element: JQuery<HTMLElement>, hidePlatformIcon: boolean): void {
    this.toggleOriginalIcon($element, !hidePlatformIcon)
    this.removeExistingIcon($element)
  }

  /**
   * 切换原始图标显示状态
   */
  private toggleOriginalIcon($element: JQuery<HTMLElement>, show: boolean): void {
    const iconSelector = this.currentConfig?.selectors.icon

    if (!iconSelector)
      return

    const $originalIcon = this.currentConfig.insertPosition === FileIconsInsertPosition.Prepend
      ? $element.find(iconSelector)
      : $element.siblings(iconSelector)

    ToolKit.dom.showControl($originalIcon, { show })
  }

  /**
   * 移除现有图标
   */
  private removeExistingIcon($element: JQuery<HTMLElement>): void {
    $element.siblings(`.${this.className}`)?.remove()
  }

  /**
   * 获取图标尺寸类
   */
  private getIconSizeClass(): string {
    switch (this.config.iconSize) {
      case FileIconSize.sm: return 'file-icon-sm'
      case FileIconSize.lg: return 'file-icon-lg'
      default: return 'file-icon-md'
    }
  }

  /**
   * 获取图标尺寸值
   */
  private getIconSizeValue(): string {
    switch (this.config.iconSize) {
      case FileIconSize.sm: return '14px'
      case FileIconSize.lg: return '18px'
      default: return '16px'
    }
  }

  /**
   * 清理操作
   */
  private cleanup(): void {
    this.processedFiles.forEach((_info, element) => {
      this.removeExistingIcon(element)
    })
    this.processedFiles.clear()
  }

  applyConfig(config: Partial<Config>): void {
    super.updateConfig(config)

    this.applyFileIcons()

    logger.info(`[${this.name}] Config updated and apply`, this.config)
  }
}
